#ifndef BLITTERS_HPP
#define BLITTERS_HPP

// Blitter Interface:
// void blitColorToPixel( ColorType& dstColor, ColorType srcColor )
// Blits srcColor into dstColor
//
// void blitColorToBuffer( ColorType* dstBuffer, ColorType srcColor, unsigned int width )
// Blits srcColor into a buffer starting at destBuffer with size = width
//
// void blitBufferToBuffer( ColorType* dstBuffer, ColorType* srcBuffer, unsigned int width )
// Blits from srcBuffer into destBuffer


template<typename ColorT>
class CopyBlitter
{
	public:
	
	typedef ColorT DstColorT;
	typedef ColorT SrcColorT;
	
	inline void blitColorToPixel( ColorT& dstColor, ColorT srcColor )
	{
		dstColor = srcColor;
	}
	
	inline void blitColorToBuffer( ColorT* dstBuffer, ColorT srcColor, unsigned int width )
	{
		for ( ; width--; ++dstBuffer )
		{
			*dstBuffer = srcColor;
		}
	}
	
	inline void blitBufferToBuffer( ColorT* dstBuffer, ColorT* srcBuffer, unsigned int width )
	{
		for ( ; width--; ++dstBuffer, ++srcBuffer )
		{
			*dstBuffer = *srcBuffer;
		}
	}
};

template<>
inline void CopyBlitter<unsigned char>::blitColorToBuffer( unsigned char* dstBuffer, unsigned char srcColor, unsigned int width )
{
	int halfFillData = ( srcColor << 8 ) | srcColor;
	int fillData = ( halfFillData << 16 ) | halfFillData;
	unsigned char* limit = dstBuffer + width;
	for( ; dstBuffer + 4 < limit; dstBuffer += 4 )
	{
		*reinterpret_cast<int*>(dstBuffer) = fillData;
	}
	
	for ( ; dstBuffer < limit; ++dstBuffer )
	{
		*dstBuffer = srcColor;
	}
}

template<>
inline void CopyBlitter<unsigned char>::blitBufferToBuffer( unsigned char* dstBuffer, unsigned char* srcBuffer, unsigned int width )
{
	unsigned char* limit = dstBuffer + width;
	for( ; dstBuffer+4 < limit; dstBuffer += 4, srcBuffer += 4 )
	{
		*reinterpret_cast<int*>(dstBuffer) = *reinterpret_cast<int*>(srcBuffer);
	}
	for ( ; dstBuffer < limit; ++dstBuffer, ++srcBuffer )
	{
		*dstBuffer = *srcBuffer;
	}
}



template<typename ColorT, ColorT Mask>
class MaskedCopyBlitter
{
	public:
	
	typedef ColorT DstColorT;
	typedef ColorT SrcColorT;
	
	static const ColorT MaskColor = Mask;

	inline void blitColorToPixel( ColorT& dstColor, ColorT srcColor )
	{
		if ( srcColor == MaskColor ) return;
		dstColor = srcColor;
	}
	
	inline void blitColorToBuffer( ColorT* dstBuffer, ColorT srcColor, unsigned int width )
	{
		if ( srcColor == MaskColor ) return;
		ColorT* limit = dstBuffer + width;
		for ( ; dstBuffer < limit; ++dstBuffer )
		{
			*dstBuffer = srcColor;
		}
	}
	
	inline void blitBufferToBuffer( ColorT* dstBuffer, ColorT* srcBuffer, unsigned int width )
	{
		ColorT* limit = dstBuffer + width;
		for ( ; dstBuffer < limit; ++dstBuffer, ++srcBuffer )
		{
			ColorT color = *srcBuffer;
			if ( color != MaskColor )
				*dstBuffer = *srcBuffer;
		}
	}
};


class AddBlitter8
{	
	public:
	
	typedef unsigned char DstColorT;
	typedef unsigned char SrcColorT;
	
	inline void blitColorToPixel( unsigned char& dstColor, unsigned char srcColor )
	{
		int tmp = srcColor + dstColor;
		if ( tmp > 0xFF ) dstColor = 0xFF;
		else dstColor = tmp;
	}
	
	inline void blitColorToBuffer( unsigned char* dstBuffer, unsigned char srcColor, unsigned int width )
	{
		int halfFillData = ( srcColor << 8 ) | srcColor;
		int fillData = ( halfFillData << 16 ) | halfFillData;
		unsigned char* limit = dstBuffer + width;
		for( ; dstBuffer + 4 < limit; dstBuffer += 4 )
		{
			// This is stolen from fblend
			unsigned int col1 = fillData;
			unsigned int col2 = *reinterpret_cast<unsigned int*>(dstBuffer);
			unsigned int tmp1 = col1 & 0x80808080;
			unsigned int tmp2 = col2 & 0x80808080;
			col1 = (col1 & 0x7F7F7F7F) + (col2 & 0x7F7F7F7F);
			
			col2 = tmp1;
			tmp1 = tmp1 | tmp2;
			tmp2 = col2 & tmp2;
			
			col2 = tmp1 & col1;
			
			col1 |= ((((tmp2 | col2) >> 7) + 0x7F7F7F7F) ^ 0x7F7F7F7F) | tmp1;
			*reinterpret_cast<int*>(dstBuffer) = col1;
		}
		
		for ( ; dstBuffer < limit; ++dstBuffer )
		{
			blitColorToPixel( *dstBuffer, srcColor );
		}
	}
	
	inline void blitBufferToBuffer( unsigned char* dstBuffer, unsigned char* srcBuffer, unsigned int width )
	{
		unsigned char* limit = dstBuffer + width;
		for( ; dstBuffer + 4 < limit; dstBuffer += 4 )
		{
			// This is stolen from fblend
			unsigned int col1 = *reinterpret_cast<unsigned int*>(srcBuffer);
			unsigned int col2 = *reinterpret_cast<unsigned int*>(dstBuffer);
			unsigned int tmp1 = col1 & 0x80808080;
			unsigned int tmp2 = col2 & 0x80808080;
			col1 = (col1 & 0x7F7F7F7F) + (col2 & 0x7F7F7F7F);
			
			col2 = tmp1;
			tmp1 = tmp1 | tmp2;
			tmp2 = col2 & tmp2;
			
			col2 = tmp1 & col1;
			
			col1 |= ((((tmp2 | col2) >> 7) + 0x7F7F7F7F) ^ 0x7F7F7F7F) | tmp1;
			*reinterpret_cast<int*>(dstBuffer) = col1;
		}
		
		for ( ; dstBuffer < limit; ++dstBuffer )
		{
			blitColorToPixel( *dstBuffer, *srcBuffer );
		}
	}
};


class AddBlitter32
{
	public:
	
	typedef unsigned int DstColorT;
	typedef unsigned int SrcColorT;
	
	inline void blitColorToPixel( unsigned int& dstColor, unsigned int srcColor )
	{
		unsigned int col1 = srcColor & 0x00FFFFFF; // Alpha channel mask
		unsigned int col2 = dstColor;
		unsigned int tmp1 = col1 & 0x80808080;
		unsigned int tmp2 = col2 & 0x80808080;
		col1 = (col1 & 0x7F7F7F7F) + (col2 & 0x7F7F7F7F);
		
		col2 = tmp1;
		tmp1 = tmp1 | tmp2;
		tmp2 = col2 & tmp2;
		
		col2 = tmp1 & col1;
		
		col1 |= ((((tmp2 | col2) >> 7) + 0x7F7F7F7F) ^ 0x7F7F7F7F) | tmp1;
		dstColor = col1;
	}
	
	inline void blitColorToBuffer( unsigned int* dstBuffer, unsigned int srcColor, unsigned int width )
	{
		int fillData = srcColor & 0x00FFFFFF; // Alpha channel mask
		for( ; width--; ++dstBuffer )
		{
			// This is stolen from fblend
			unsigned int col1 = fillData;
			unsigned int col2 = *dstBuffer;
			unsigned int tmp1 = col1 & 0x80808080;
			unsigned int tmp2 = col2 & 0x80808080;
			col1 = (col1 & 0x7F7F7F7F) + (col2 & 0x7F7F7F7F);
			
			col2 = tmp1;
			tmp1 = tmp1 | tmp2;
			tmp2 = col2 & tmp2;
			
			col2 = tmp1 & col1;
			
			col1 |= ((((tmp2 | col2) >> 7) + 0x7F7F7F7F) ^ 0x7F7F7F7F) | tmp1;
			*dstBuffer = col1;
		}
	}
	
	inline void blitBufferToBuffer( unsigned int* dstBuffer, unsigned int* srcBuffer, unsigned int width )
	{
		for( ; width--; ++dstBuffer, ++srcBuffer )
		{
			// This is stolen from fblend
			unsigned int col1 = *srcBuffer & 0x00FFFFFF; // Alpha channel mask
			unsigned int col2 = *dstBuffer;
			unsigned int tmp1 = col1 & 0x80808080;
			unsigned int tmp2 = col2 & 0x80808080;
			col1 = (col1 & 0x7F7F7F7F) + (col2 & 0x7F7F7F7F);
			
			col2 = tmp1;
			tmp1 = tmp1 | tmp2;
			tmp2 = col2 & tmp2;
			
			col2 = tmp1 & col1;
			
			col1 |= ((((tmp2 | col2) >> 7) + 0x7F7F7F7F) ^ 0x7F7F7F7F) | tmp1;
			*dstBuffer = col1;
		}
	}
};


class ScaledAddBlitter32
{
	public:
	
	typedef unsigned int DstColorT;
	typedef unsigned int SrcColorT;
	
	ScaledAddBlitter32( unsigned int scale_ ) : scale(scale_)
	{}
	
	inline void blitColorToPixel( unsigned int& dstColor, unsigned int srcColor )
	{
		unsigned int col1 = srcColor & 0x00FF00FF; // Alpha channel mask
		unsigned int col1_2 = srcColor & 0x0000FF00;
		
		// Scale the source
		col1 = (((col1 * scale) >> 8) & 0xFF00FF);
		col1_2 =  (((col1_2 * scale) >> 8) & 0x00FF00);
		col1 |= col1_2;
		
		unsigned int col2 = dstColor;
		unsigned int tmp1 = col1 & 0x80808080;
		unsigned int tmp2 = col2 & 0x80808080;
		col1 = (col1 & 0x7F7F7F7F) + (col2 & 0x7F7F7F7F);
		
		col2 = tmp1;
		tmp1 = tmp1 | tmp2;
		tmp2 = col2 & tmp2;
		
		col2 = tmp1 & col1;
		
		col1 |= ((((tmp2 | col2) >> 7) + 0x7F7F7F7F) ^ 0x7F7F7F7F) | tmp1;
		dstColor = col1;
	}
	
	inline void blitColorToBuffer( unsigned int* dstBuffer, unsigned int srcColor, unsigned int width )
	{
		int fillData = srcColor & 0x00FF00FF; // Alpha channel mask
		unsigned int fillData2 = srcColor & 0x0000FF00;
		
		// Scale the source
		fillData = (((fillData * scale) >> 8) & 0xFF00FF);
		fillData2 =  (((fillData2 * scale) >> 8) & 0x00FF00);
		fillData |= fillData2;
		
		for( ; width--; ++dstBuffer )
		{
			// This is stolen from fblend
			unsigned int col1 = fillData;
			unsigned int col2 = *dstBuffer;
			unsigned int tmp1 = col1 & 0x80808080;
			unsigned int tmp2 = col2 & 0x80808080;
			col1 = (col1 & 0x7F7F7F7F) + (col2 & 0x7F7F7F7F);
			
			col2 = tmp1;
			tmp1 = tmp1 | tmp2;
			tmp2 = col2 & tmp2;
			
			col2 = tmp1 & col1;
			
			col1 |= ((((tmp2 | col2) >> 7) + 0x7F7F7F7F) ^ 0x7F7F7F7F) | tmp1;
			*dstBuffer = col1;
		}
	}
	
	inline void blitBufferToBuffer( unsigned int* dstBuffer, unsigned int* srcBuffer, unsigned int width )
	{
		for( ; width--; ++dstBuffer, ++srcBuffer )
		{
			unsigned int col1 = *srcBuffer & 0x00FF00FF; // Alpha channel mask
			unsigned int col1_2 = *srcBuffer & 0x0000FF00;
			
			// Scale the source
			col1 = (((col1 * scale) >> 8) & 0x00FF00FF);
			col1_2 =  (((col1_2 * scale) >> 8) & 0x0000FF00);
			col1 |= col1_2;
		
			unsigned int col2 = *dstBuffer;
			unsigned int tmp1 = col1 & 0x80808080;
			unsigned int tmp2 = col2 & 0x80808080;
			col1 = (col1 & 0x7F7F7F7F) + (col2 & 0x7F7F7F7F);
			
			col2 = tmp1;
			tmp1 = tmp1 | tmp2;
			tmp2 = col2 & tmp2;
			
			col2 = tmp1 & col1;
			
			col1 |= ((((tmp2 | col2) >> 7) + 0x7F7F7F7F) ^ 0x7F7F7F7F) | tmp1;
			*dstBuffer = col1;
		}
	}
	
	unsigned int scale;
};

template< unsigned int Mask = 0xFFFF00FF >
class MaskedTransBlitter32
{
	public:
	
	typedef unsigned int DstColorT;
	typedef unsigned int SrcColorT;
	
	static const unsigned int MaskColor = Mask;
	
	MaskedTransBlitter32( unsigned int scale_ ) : scale(scale_)
	{}
	
	inline void blitColorToPixel( unsigned int& dstColor, unsigned int srcColor )
	{
		if ( srcColor == MaskColor ) return;
		unsigned int srcRB = srcColor & 0x00FF00FF;
		unsigned int srcG = srcColor & 0x0000FF00;
		
		srcRB = ((srcRB * scale) >> 8) & 0x00FF00FF;
		srcG =  ((srcG * scale) >> 8) & 0x0000FF00;
		
		unsigned int invScale = 256 - scale;
		
		unsigned int col1 = dstColor;
		unsigned int tmp1 = col1 & 0x00FF00FF;
		col1 &= 0x0000FF00;
		
		tmp1 = (((tmp1 * invScale) >> 8 ) + srcRB ) & 0x00FF00FF;
		col1 = (((col1 * invScale) >> 8 ) + srcG ) & 0x0000FF00;
		
		col1 |= dstColor & 0xFF000000; // conserve alpha
		
		dstColor = col1 | tmp1;
	}
	
	inline void blitColorToBuffer( unsigned int* dstBuffer, unsigned int srcColor, unsigned int width )
	{
		if ( srcColor == MaskColor ) return;
		unsigned int srcRB = srcColor & 0x00FF00FF;
		unsigned int srcG = srcColor & 0x0000FF00;
		
		srcRB = ((srcRB * scale) >> 8) & 0x00FF00FF;
		srcG =  ((srcG * scale) >> 8) & 0x0000FF00;
		
		unsigned int invScale = 256 - scale;
		
		for( ; width--; ++dstBuffer )
		{
			unsigned int col1 = *dstBuffer;
			unsigned int tmp1 = col1 & 0x00FF00FF;
			col1 &= 0x0000FF00;
			
			tmp1 = (((tmp1 * invScale) >> 8 ) + srcRB ) & 0x00FF00FF;
			col1 = (((col1 * invScale) >> 8 ) + srcG ) & 0x0000FF00;
			
			col1 |= *dstBuffer & 0xFF000000; // conserve alpha
			
			*dstBuffer = col1 | tmp1;
		}
	}
	
	inline void blitBufferToBuffer( unsigned int* dstBuffer, unsigned int* srcBuffer, unsigned int width )
	{
		unsigned int invScale = 256 - scale;
		
		for( ; width--; ++dstBuffer, ++srcBuffer )
		{
			if ( *srcBuffer == MaskColor ) continue;
			unsigned int srcRB = *srcBuffer & 0x00FF00FF;
			unsigned int srcG = *srcBuffer & 0x0000FF00;
			
			srcRB = ((srcRB * scale) >> 8) & 0x00FF00FF;
			srcG =  ((srcG * scale) >> 8) & 0x0000FF00;
			
			unsigned int col1 = *dstBuffer;
			unsigned int tmp1 = col1 & 0x00FF00FF;
			col1 &= 0x0000FF00;
			
			tmp1 = (((tmp1 * invScale) >> 8 ) + srcRB ) & 0x00FF00FF;
			col1 = (((col1 * invScale) >> 8 ) + srcG ) & 0x0000FF00;
			
			col1 |= *dstBuffer & 0xFF000000; // conserve alpha
			
			*dstBuffer = col1 | tmp1;
		}
	}
	
	unsigned int scale;
};

class ScaleBlitter8to32
{
	public:
	
	typedef unsigned int DstColorT;
	typedef unsigned char SrcColorT;
	
	inline void blitColorToPixel( unsigned int& dstColor, unsigned char srcColor )
	{
		unsigned int dstRB = dstColor & 0x00FF00FF;
		unsigned int dstG = dstColor & 0x0000FF00;
		
		dstRB = ((dstRB * srcColor) >> 8) & 0x00FF00FF;
		dstG =  ((dstG * srcColor) >> 8) & 0x0000FF00;
		
		dstColor = ( dstColor & 0xFF000000 ) | ( dstRB | dstG );
	}
	
	inline void blitColorToBuffer( unsigned int* dstBuffer, unsigned char srcColor, unsigned int width )
	{
		for( ; width--; ++dstBuffer )
		{
			unsigned int dstColor = *dstBuffer;
			unsigned int dstRB = dstColor & 0x00FF00FF;
			unsigned int dstG = dstColor & 0x0000FF00;
			
			dstRB = ((dstRB * srcColor) >> 8) & 0x00FF00FF;
			dstG =  ((dstG * srcColor) >> 8) & 0x0000FF00;
			
			*dstBuffer = ( dstColor & 0xFF000000 ) | ( dstRB | dstG );
		}
	}
	
	inline void blitBufferToBuffer( unsigned int* dstBuffer, unsigned char* srcBuffer, unsigned int width )
	{
		for( ; width--; ++dstBuffer, ++srcBuffer )
		{
			unsigned char srcColor = *srcBuffer;
			unsigned int dstColor = *dstBuffer;
			unsigned int dstRB = dstColor & 0x00FF00FF;
			unsigned int dstG = dstColor & 0x0000FF00;
			
			dstRB = ((dstRB * srcColor) >> 8) & 0x00FF00FF;
			dstG =  ((dstG * srcColor) >> 8) & 0x0000FF00;
			
			*dstBuffer = ( dstColor & 0xFF000000 ) | ( dstRB | dstG );
		}
	}
};


#endif // BLITTERS_HPP
